
import java.util.Vector;

import javax.microedition.lcdui.Canvas;
import javax.microedition.lcdui.Font;
import javax.microedition.lcdui.Graphics;

public class ScrollableTable
{
	private int currentCol;
	private int currentRow;
	
	private int rows;
	private int cols;
	private int[] colWidths;
	private int[] rowHeights;
	private int[] colLeft;
	private int[] rowTop;
	
	private String[][] data;
	private String[][][] dataRows;
	
	private int viewportWidth;
	private int viewportHeight;
	private int viewportX;
	private int viewportY;
	private int width;
	private int height;
	
	private Font font;
	
	private int bgColor;
	private int bgFocusedColor;
	private int foreColor;
	private int borderWidth;
	private int borderColor;
	private int interline;
	private int padding;
	
	public ScrollableTable(String[][] data, int width, int height){
		this.viewportWidth = width;
		this.viewportHeight = height;
		
		this.currentCol = 0;
		this.currentRow = 0;
		
		this.rows = 0;
		this.cols = 0;
		
		this.font = Font.getDefaultFont();
		
		this.bgColor =Constants.BLACK;
		this.bgFocusedColor =Constants.BLUE;
		this.foreColor= Constants.WHITE;
		this.borderColor =Constants.WHITE;
		
		this.borderWidth = 1;
		this.interline = 2;
		this.padding = 2;
		
		setData(data);
	}
	
	public void setData(String[][] data) {
		this.data = data;
		
		this.rows = this.data.length;
		this.cols = this.data[0].length;
		
		initialize();
	}
	
	void initialize() {
		this.rowTop = new int[rows];
		this.colLeft = new int[cols];
		
		this.colWidths = new int[cols];
		this.dataRows = new String[rows][][];
		
		int mediumWidth = (viewportWidth - cols * (2 * padding + borderWidth) - borderWidth) / cols;
		int extraWidth = 0;
		int overflowCols = 0;
		
		for(int i = 0; i < cols; i++) {
			int colWidth = 0;
			
			for(int j = 0; j < rows; j++) {
				colWidth = Math.max(
					colWidth,
					font.stringWidth(data[j][i])
				);
			}
			if(colWidth < mediumWidth) {
				colWidths[i] = colWidth;
				
				extraWidth += (mediumWidth - colWidth);
			}
			else {
				colWidths[i] = mediumWidth;
				
				overflowCols++;
			}
		}

		for(int i = 0; i < cols; i++) {
			if(overflowCols > 0 && extraWidth > 0 && colWidths[i] == mediumWidth)
			{
				colWidths[i] += (extraWidth / overflowCols);
			}
			colLeft[i] = width;
			colWidths[i] += + 2 * padding;
			
			width += colWidths[i] + borderWidth;
		}
		width += borderWidth;
		
		rowHeights = new int[rows];
		
		for(int i = 0; i < rows; i++) {
			this.dataRows[i] = new String[cols][];
			
			for(int j = 0; j < cols; j++) {
				this.dataRows[i][j] = getTextRows(this.data[i][j], font, colWidths[j]);
				
				rowHeights[i] = Math.max(
					rowHeights[i],
					dataRows[i][j].length * (font.getHeight() + interline) - (dataRows[i][j].length > 0 ? interline : 0)
				);
			}
			rowHeights[i] += 2 * padding;
			rowTop[i] = height;
			
			height += rowHeights[i] + borderWidth;
		}
		
		height += borderWidth;
	}
	
	public void paint(Graphics g) {
		g.setClip(0, 0, viewportWidth, viewportHeight);
		
		g.translate(- viewportX, - viewportY);
		
		g.setColor(bgColor);
		g.fillRect(0, 0, width, height);
		
		int currentX = 0;
		int currentY = 0;
		
		for(int i = 0; i < rows; i++) {
			currentX = 0;
			
			g.setColor(borderColor);
			
			g.fillRect(0, currentY, width, borderWidth);
			
			currentY += borderWidth;
			
			for(int j = 0; j < cols; j++) {
				if(i == 0) {
					g.setColor(borderColor);
					
					g.fillRect(currentX, 0, borderWidth, height);
				}
				if(j == currentCol && i == currentRow) {
					g.setColor(bgFocusedColor);
					
					g.fillRect(borderWidth + currentX, currentY, colWidths[j], rowHeights[i]);
					
					g.setColor(foreColor);
				}
				
				currentX += borderWidth;
				
				g.setColor(foreColor);
				
				for(int k = 0; k < dataRows[i][j].length; k++) {
					g.drawString(dataRows[i][j][k], padding + currentX, padding + currentY + k * (font.getHeight() + interline), Graphics.TOP | Graphics.LEFT);
				}
				currentX += colWidths[j];
			}
			if(i == 0) {
				g.setColor(borderColor);
				
				g.fillRect(currentX, 0, borderWidth, height);
			}
			
			currentY += rowHeights[i];
		}
		g.setColor(borderColor);
		
		g.fillRect(0, currentY, width, borderWidth);
		
		g.translate(viewportX, viewportY);
	}
	
	public String getSelectedData() {
		return data[currentRow][currentCol];
	}
	
	public void keyPressed(int keyCode) {
		switch(keyCode){
		case Canvas.UP:
			move(0, -1);
			break;
		case Canvas.DOWN:
			move(0, 1);
			break;
		case Canvas.RIGHT:
			move(1, 0);
			break;
		case Canvas.LEFT:
			move(-1, 0);
			break;
		}
	}
	
	boolean scroll(int colDelta, int rowDelta) {
		boolean scrolled = false;
		
		if(colDelta > 0 && colLeft[currentCol] + colWidths[currentCol] > viewportX + viewportWidth) {
			viewportX += Constants.SCROLL_STEP;
			
			scrolled = true;
		}
		else if(colDelta < 0 && colLeft[currentCol] < viewportX) {
			viewportX -= Constants.SCROLL_STEP;
			
			scrolled = true;
		}
		if(rowDelta > 0 && rowTop[currentRow] + rowHeights[currentRow] > viewportY + viewportHeight) {
			viewportY += Constants.SCROLL_STEP;
			
			scrolled = true;
		}
		else if(rowDelta < 0 && rowTop[currentRow] < viewportY) {
			viewportY -= Constants.SCROLL_STEP;
			
			scrolled = true;
		}
		if(scrolled) {
			viewportX = Math.max(0, Math.min(viewportX, width - viewportWidth));
			viewportY = Math.max(0, Math.min(viewportY, height - viewportHeight));
		}
		return scrolled;
	}
	
	void move(int colDelta, int rowDelta) {
		if(!scroll(colDelta, rowDelta)) {
			int newCol = currentCol + colDelta;
			int newRow = currentRow + rowDelta;
			
			if(newCol >= 0 && newRow >= 0 && newRow < rows && newCol < cols) {
				currentCol = newCol;
				currentRow = newRow;
				
				scroll(colDelta, rowDelta);
			}
		}
	}
	
	static String[] getTextRows(String text, Font font, int width) {
		int prevIndex = 0;
		int currIndex = text.indexOf(Constants.SPACE_CHAR);

		Vector rowsVector = new Vector();
		
    	StringBuffer stringBuffer = new StringBuffer();
    	
    	String currentToken;
    	
    	String currentRowText = Constants.VOID_STRING;

    	while(prevIndex != -1) {
    		int startCharIndex = prevIndex == 0 ? prevIndex : prevIndex + 1;
    		
    		if(currIndex != -1)
    			currentToken = text.substring(startCharIndex, currIndex);
    		else
    			currentToken = text.substring(startCharIndex);
    		
    		prevIndex = currIndex;
    		
    		currIndex = text.indexOf(Constants.SPACE_CHAR, prevIndex + 1);
    		
    		if(currentToken.length() == 0) {
    			continue;
    		}
    		
    		if(stringBuffer.length() > 0)
        		stringBuffer.append(Constants.SPACE_CHAR);
    		
    		stringBuffer.append(currentToken);
    			
    	    if(font.stringWidth(stringBuffer.toString()) > width) {
    	    	if(currentRowText.length() > 0) {
    	    		rowsVector.addElement(currentRowText);
    	    	}
    	        stringBuffer.setLength(0);
    	        
    	        currentRowText = Constants.VOID_STRING;
    	        
    	        stringBuffer.append(currentToken);
    	        	
    	        currentRowText = stringBuffer.toString();
    	    }
    	    else {
    	    	currentRowText = stringBuffer.toString();
    	    }
    	}
    	if(currentRowText.length() > 0) {
	    	rowsVector.addElement(currentRowText);
	    }
		String[] rowsArray = new String[rowsVector.size()];
		
		rowsVector.copyInto(rowsArray);
	
		return rowsArray;
	}
}
